package it.dornauf.win.util;

/**
   $Id: $ 
   Copyright [2004, 2013] [Oliver Dornauf, pda-systems.COM]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
import java.lang.reflect.Method;
import java.util.prefs.Preferences;

/**
 * <p>
 * Reads and write Windows Registry Keys by the means of the Java <code>Preference</code> implementation for the windows
 * sdk/jre.
 * 
 * @author Oliver
 * @see http://kickjava.com/src/java/util/prefs/WindowsPreferences.java.htm
 */
public class WindowsPreferencesWrapper
{
    private static WindowsPreferencesWrapper instance;
    /* Windows security masks */
    private static final int DELETE = 0x10000;
    private static final int KEY_QUERY_VALUE = 1;
    private static final int KEY_SET_VALUE = 2;
    private static final int KEY_CREATE_SUB_KEY = 4;
    private static final int KEY_ENUMERATE_SUB_KEYS = 8;
    private static final int KEY_READ = 0x20019;
    private static final int KEY_WRITE = 0x20006;
    private static final int KEY_ALL_ACCESS = 0xf003f;

    private Method methodCloseKey;
    private Method methodOpenKey;
    private Method methodWindowsRegQueryValueEx;
    private Method stringToByteArray;
    private final Preferences HKEY_CURRENT_USER = Preferences.userRoot();
    private final Preferences HKEY_LOCAL_MACHINE = Preferences.systemRoot();

    /**
     * <p>returns sole instance (threadsafe)</p>
     * @return
     */
    public static synchronized WindowsPreferencesWrapper getInstance() {
        if (instance == null) {
            instance = new WindowsPreferencesWrapper();
        }
        return instance;
    }

    /**
     * <p>
     * Reads a HKEY_CURRENT_USER Registry Subkey.
     * </p>
     * 
     * @see http://support.microsoft.com/kb/310595/en-us/
     * @param path
     * @param key
     * @return either the registries key value or an empty string
     */
    public String getHKCUKeyValue(String path, String key) {

        return getRegistryKeyValue(HKEY_CURRENT_USER, path, key);
    }

    /**
     * <p>
     * Reads a HKEY_LOCAL_MACHINE Registry Subkey.
     * </p>
     * 
     * @see http://support.microsoft.com/kb/250405/en-us
     * @param path
     * @param key
     * @return either the registries key value or an empty string
     */
    public String getHKLMKeyValue(String path, String key) {

        return getRegistryKeyValue(HKEY_LOCAL_MACHINE, path, key);
    }

    private WindowsPreferencesWrapper() {
        init();
    }
    
    private String getRegistryKeyValue(Preferences rootNode, String path, String key) {

        String keyValue = "";

        try {
            byte[] lpszPath = (byte[]) stringToByteArray.invoke(rootNode, path);
            byte[] lpszKey = (byte[]) stringToByteArray.invoke(rootNode, key);
            Integer openedKeyHandle = (Integer) methodOpenKey.invoke(rootNode, lpszPath, KEY_READ, KEY_READ);
            byte[] bytes = (byte[]) methodWindowsRegQueryValueEx.invoke(rootNode, openedKeyHandle.intValue(), lpszKey);
            keyValue = (bytes != null ? new String(bytes).trim() : null);
            methodCloseKey.invoke(HKEY_CURRENT_USER, openedKeyHandle);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return keyValue;
    }

    private void init() {

        try {
            Class<? extends Preferences> windowsPreferences = HKEY_CURRENT_USER.getClass();

            methodOpenKey = windowsPreferences.getDeclaredMethod("openKey", byte[].class, int.class, int.class);
            methodOpenKey.setAccessible(true);

            methodCloseKey = windowsPreferences.getDeclaredMethod("closeKey", int.class);
            methodCloseKey.setAccessible(true);

            methodWindowsRegQueryValueEx = windowsPreferences.getDeclaredMethod("WindowsRegQueryValueEx", int.class,
                    byte[].class);
            methodWindowsRegQueryValueEx.setAccessible(true);

            stringToByteArray = windowsPreferences.getDeclaredMethod("stringToByteArray", String.class);
            stringToByteArray.setAccessible(true);

        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}
